//********************************************************************
//	created:	20:10:2011   11:40
//	filename: 	mysqlstatement.h
//	author:		tiamo
//	purpose:	mysql statement
//********************************************************************

#pragma once

namespace mysql
{
	//
	// forward decl
	//
	class Database;

	//
	// traits
	//
	template<typename Type> struct Traits									{};

	//
	// traits(int8_t)
	//
	template<> struct Traits<char>											{enum {mBufferType = MYSQL_TYPE_TINY, mBufferLength = sizeof(int8_t), mIsUnsigned = 0};};

	//
	// traits(uint8_t)
	//
	template<> struct Traits<uint8_t>										{enum {mBufferType = MYSQL_TYPE_TINY, mBufferLength = sizeof(uint8_t), mIsUnsigned = 1};};

	//
	// traits(int16_t)
	//
	template<> struct Traits<int16_t>										{enum {mBufferType = MYSQL_TYPE_SHORT, mBufferLength = sizeof(int16_t), mIsUnsigned = 0};};

	//
	// traits(uint16_t)
	//
	template<> struct Traits<uint16_t>										{enum {mBufferType = MYSQL_TYPE_SHORT, mBufferLength = sizeof(uint16_t), mIsUnsigned = 1};};

	//
	// traits(int32_t)
	//
	template<> struct Traits<int32_t>										{enum {mBufferType = MYSQL_TYPE_LONG, mBufferLength = sizeof(int32_t), mIsUnsigned = 0};};

	//
	// traits(uint32_t)
	//
	template<> struct Traits<uint32_t>										{enum {mBufferType = MYSQL_TYPE_LONG, mBufferLength = sizeof(uint32_t), mIsUnsigned = 1};};

	//
	// traits(int64_t)
	//
	template<> struct Traits<int64_t>										{enum {mBufferType = MYSQL_TYPE_LONGLONG, mBufferLength = sizeof(int64_t), mIsUnsigned = 0};};

	//
	// traits(uint64_t)
	//
	template<> struct Traits<uint64_t>										{enum {mBufferType = MYSQL_TYPE_LONGLONG, mBufferLength = sizeof(uint64_t), mIsUnsigned = 1};};

	//
	// statement
	//
	class Statement
	{
	public:
		//
		// constructor
		//
		Statement(MYSQL_STMT* statement, Database& database);

		//
		// destructor
		//
		~Statement()														{close();}

		//
		// move constructor
		//
		Statement(Statement&& rhs) : mDatabase(rhs.mDatabase)				{mStatement = rhs.mStatement; rhs.mStatement = nullptr;}

		//
		// reset
		//
		void reset(bool resetStatement, bool resetBind);

		//
		// prepare
		//
		void prepareV(wchar_t const* statementFormat, ...);

		//
		// prepare
		//
		void prepare(wchar_t const* statementString);

		//
		// bind param/result
		//
		template<typename ValueType>
		void bind(wchar_t const* fieldName, bool bindParam, ValueType& theValue, bool* isNull = nullptr)
		{
			bind(fieldName, bindParam, static_cast<enum_field_types>(Traits<ValueType>::mBufferType), &theValue, Traits<ValueType>::mBufferLength, Traits<ValueType>::mIsUnsigned, nullptr, reinterpret_cast<my_bool*>(isNull));
		}

		//
		// bind string
		//
		void bind(wchar_t const* fieldName, bool bindParam, UniString& theValue, uint32_t reserveLength = 255, bool* isNull = nullptr);

		//
		// bind param/result
		//
		void bind(wchar_t const* fieldName, bool bindParam, enum_field_types bufferType, void* valueBuffer, uint32_t bufferLength, my_bool isUnsigned, unsigned long* valueLength, my_bool* isNull);

		//
		// execute
		//
		void execute();

		//
		// fetch
		//
		bool fetch(bool failIfTruncated = false);

		//
		// close
		//
		void close();

	private:
		//
		// disable copy/assignment
		//
		Statement(Statement const& rhs);
		Statement& operator=(Statement const& rhs);

	private:
		//
		// database
		//
		Database&															mDatabase;

		//
		// statement
		//
		MYSQL_STMT*															mStatement;

		//
		// param
		//
		utils::DataBuffer													mParamBindBuffer;

		//
		// result
		//
		utils::DataBuffer													mResultBindBuffer;

		//
		// temp string
		//
		std::map<size_t, std::pair<utils::DataBuffer, unsigned long>>		mTempStringList;
	};
}
